import { Resume } from '@/models/resume.model';
import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { Sequelize } from 'sequelize-typescript';
import { Content } from '@/models/content.model';
import { Entry } from '@/models/entry.model';
import * as fs from 'fs';
import * as path from 'path';
import { Customization } from '@/models/customization.model';
import * as sd from 'silly-datetime';
import {
  ApplyTemplateDto,
  EditSectionNameDto,
  EntryDto,
  PersonalDetailDto,
  SortEntryDto,
  ToggleHideEntryDto,
  UpdateCustomizationDto,
  addContentParamsDto,
  addCustomizationDto,
  addResumeParamsDto,
  delContentParamsDto,
  editContentParamsDto,
  getResumeParamsDto,
} from './dto';
import {
  generatePdf,
  generatePdfByUrl,
  responseMessage,
  updateValue,
} from '@/utils';
import { REQUEST } from '@nestjs/core';
import { TemplateService } from '../template_list/template_list.service';
import { DownloadLog } from '@/models/downloadLog.model';
import { Op } from 'sequelize';
@Injectable()
export class ResumeService {
  constructor(
    @InjectModel(Resume)
    private readonly resumeModel: typeof Resume,
    @InjectModel(Content)
    private readonly contentModel: typeof Content,
    @InjectModel(Entry)
    private readonly entryModel: typeof Entry,
    @InjectModel(Customization)
    private readonly customization: typeof Customization,

    @InjectModel(DownloadLog)
    private readonly downloadLog: typeof DownloadLog,

    private sequelize: Sequelize,

    @Inject(REQUEST)
    private readonly request: Request & { user: { userId: number } },

    private readonly templateService: TemplateService,
  ) {}
  async getResumeDetail({ id }: getResumeParamsDto): Promise<any> {
    const resumes = await this.resumeModel.findOne({
      attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
      // 联表查询
      include: [
        {
          model: Content,
          as: 'contents',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
          include: [
            {
              model: Entry,
              as: 'entries',
              attributes: {
                exclude: ['created_time', 'updated_time', 'order', 'userId'],
              },
            },
          ],
        },
        {
          model: Customization,
          as: 'customization',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
        },
      ],
      order: [['contents', 'entries', 'order', 'asc']],
      where: {
        id,
        userId: this.request.user?.userId,
      },
    });
    if (!resumes) {
      return await this.templateService.getTemplate({ default: 1 });
    }
    const contentResult = {};
    try {
      resumes.contents.forEach((content) => {
        contentResult[content.sectionType] = {
          ...content?.dataValues,
        };
      });
    } catch {}

    let {
      // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const
      contents,
      customization,
      // eslint-disable-next-line prefer-const
      ...personalDetails
    } = resumes?.dataValues || {};
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { showPhoto, photo, ...personalDetail } = personalDetails;
    if (!customization) {
      const template = await this.templateService.getTemplate({ default: 1 });
      customization = template.customization;
    } else {
      const {
        header,
        layout,
        spacing,
        colors,
        applyAccentColor,
        title,
        // defaultSectionOrder,
      } = customization;

      if (
        !colors ||
        !header ||
        !spacing ||
        !layout ||
        !applyAccentColor ||
        !title
        // !defaultSectionOrder
      ) {
        const template = await this.templateService.getTemplate({ default: 1 });
        const initCustomization = template.customization;
        if (!colors) {
          customization.colors = initCustomization.colors;
        }
        if (!header) {
          customization.header = initCustomization.header;
        }
        if (!spacing) {
          customization.spacing = initCustomization.spacing;
        }
        if (!layout) {
          customization.layout = initCustomization.layout;
        }
        if (!applyAccentColor) {
          customization.applyAccentColor = initCustomization.applyAccentColor;
        }
        if (!title) {
          customization.title = initCustomization.title;
        }
        // if (!defaultSectionOrder) {
        //   customization.defaultSectionOrder =
        //     initCustomization.defaultSectionOrder;
        // }
      }
    }
    const contentkeys = Object.keys(contentResult);
    const { sectionOrder } = customization;
    if (contentkeys.length > sectionOrder.one.leftSectionChildren.length) {
      sectionOrder.one.leftSectionChildren = [
        ...new Set([...sectionOrder.one.leftSectionChildren, ...contentkeys]),
      ];
    }
    if (
      contentkeys.length >
      sectionOrder.two.leftSectionChildren.length +
        sectionOrder.two.rightSectionChildren.length
    ) {
      const total = [
        ...sectionOrder.two.leftSectionChildren,
        ...sectionOrder.two.rightSectionChildren,
      ];
      if (
        sectionOrder.two.leftSectionChildren.length >
        sectionOrder.two.rightSectionChildren.length
      ) {
        const keys = contentkeys.filter(
          (v) =>
            !total.includes(v) &&
            !sectionOrder.two.rightSectionChildren.includes(v),
        );
        sectionOrder.two.leftSectionChildren = [
          ...new Set([...sectionOrder.two.leftSectionChildren, ...keys]),
        ];
        sectionOrder.two.rightSectionChildren =
          sectionOrder.two.rightSectionChildren.filter(
            (v) => !sectionOrder.two.leftSectionChildren.includes(v),
          );
      } else {
        const keys = contentkeys.filter(
          (v) =>
            !total.includes(v) &&
            !sectionOrder.two.leftSectionChildren.includes(v),
        );
        sectionOrder.two.rightSectionChildren = [
          ...new Set([...sectionOrder.two.rightSectionChildren, ...keys]),
        ];
        sectionOrder.two.leftSectionChildren =
          sectionOrder.two.leftSectionChildren.filter(
            (v) => !sectionOrder.two.rightSectionChildren.includes(v),
          );
      }
    }
    return {
      personalDetails: {
        ...personalDetail,
      },
      content: contentResult,
      customization: customization,
    };
  }

  async getResumes(): Promise<any> {
    const resumeList = await this.resumeModel.findAll({
      attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
      // 联表查询
      include: [
        {
          model: Content,
          as: 'contents',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
          include: [
            {
              model: Entry,
              as: 'entries',
              attributes: {
                exclude: ['created_time', 'updated_time', 'order', 'userId'],
              },
            },
          ],
        },
        {
          model: Customization,
          as: 'customization',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
        },
      ],
      order: [['id', 'desc']],
      where: {
        userId: this.request.user?.userId,
        [Op.or]: [{ deleted: null }, { deleted: { [Op.ne]: 1 } }],
      },
    });
    const formatResume = (resumes) => {
      const contentResult = {};
      try {
        resumes.contents.forEach((content) => {
          contentResult[content.sectionType] = {
            ...content?.dataValues,
          };
        });
      } catch {}

      const {
        // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const
        contents,
        customization,
        // eslint-disable-next-line prefer-const
        ...personalDetails
      } = resumes?.dataValues || {};
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { showPhoto, photo, ...personalDetail } = personalDetails;

      return {
        personalDetails: {
          ...personalDetail,
        },
        content: contentResult,
        customization: customization,
      };
    };
    const list = [];
    try {
      for (let i = 0; i < resumeList.length; i++) {
        list.push(formatResume(resumeList[i]));
      }
    } catch {}

    // const {
    //   // eslint-disable-next-line @typescript-eslint/no-unused-vars, prefer-const
    //   contents,
    //   customization,
    //   // eslint-disable-next-line prefer-const
    //   ...personalDetails
    // } = resumes?.dataValues || {};
    return list;
  }

  async validResume(): Promise<any> {
    const resume = await this.resumeModel.findOne({
      attributes: { include: ['id'] },
      where: {
        userId: this.request.user?.userId,
        [Op.or]: [{ deleted: null }, { deleted: { [Op.ne]: 1 } }],
      },
    });
    return !!resume;
  }

  async getResumeDetailWithoutGuards({ id }: getResumeParamsDto): Promise<any> {
    const resumes = await this.resumeModel.findOne({
      attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
      // 联表查询
      include: [
        {
          model: Content,
          as: 'contents',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
          include: [
            {
              model: Entry,
              as: 'entries',
              attributes: {
                exclude: ['created_time', 'updated_time', 'order', 'userId'],
              },
            },
          ],
        },
        {
          model: Customization,
          as: 'customization',
          attributes: { exclude: ['created_time', 'updated_time', 'userId'] },
        },
      ],
      order: [['contents', 'entries', 'order', 'asc']],
      where: {
        id,
      },
    });
    if (!resumes) {
      return null;
    }
    const contentResult = {};
    try {
      resumes.contents.forEach((content) => {
        contentResult[content.sectionType] = {
          ...content?.dataValues,
        };
      });
    } catch {}
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { contents, customization, ...personalDetails } =
      resumes?.dataValues || {};
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { showPhoto, photo, ...personalDetail } = personalDetails;
    return {
      personalDetails: {
        ...personalDetail,
      },
      content: contentResult,
      customization: customization,
    };
  }
  async addResumeDetail(params: addResumeParamsDto): Promise<any> {
    const { content, personalDetails, customization } = params;
    if (!this.request.user?.userId) {
      return responseMessage({}, '用户不存在!', -1);
    }
    const res = await this.resumeModel.findOne({
      where: {
        userId: this.request.user.userId,
      },
    });
    if (res) {
      if (process.env.NODE_ENV !== 'development') {
        return responseMessage(null, '目前仅允许创建一份简历', -1);
      }
    }
    // const template = await this.templateService.getTemplate({
    //   id: customization.templateId,
    // });
    const t = await this.sequelize.transaction();
    try {
      const result = await this.resumeModel.create(
        {
          ...personalDetails,
          gender:
            personalDetails.gender + '' === '' ? null : personalDetails.gender,
          birthDay:
            personalDetails.birthDay + '' === ''
              ? null
              : personalDetails.birthDay,
          userId: this.request.user.userId,
        },
        {
          transaction: t,
        },
      );
      await this.customization.create(
        {
          ...customization,
          // sectionOrder: (template.customization as any).sectionOrder,
          resumeId: result.id,
          userId: this.request.user.userId,
        },
        {
          transaction: t,
        },
      );

      const contentData = content.map((item) => {
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { entries, ...restItem } = item;
        return {
          ...restItem,
          resumeId: result.id,
          userId: this.request.user.userId,
        };
      });

      const contentResults = await this.contentModel.bulkCreate(contentData, {
        transaction: t,
      });

      const contentIds = contentResults.map((record) => record.id);

      const entryData = content.flatMap((section, index) => {
        const contentId = contentIds[index];
        return section.entries.map((item, index) => ({
          ...item,
          contentId: contentId,
          order: index,
          userId: this.request.user.userId,
        }));
      });
      await this.entryModel.bulkCreate(entryData, {
        transaction: t,
      });
      await t.commit();
      return result.id;
    } catch (error) {
      // 如果执行到达此行,则抛出错误,回滚事务
      await t.rollback();
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  async addContent(params: addContentParamsDto): Promise<any> {
    const { resumeId, sectionOrder, content } = params;
    const { entries, ...restItem } = content;
    const t = await this.sequelize.transaction();
    try {
      const contentData = {
        ...restItem,
        resumeId,
        userId: this.request.user.userId,
      };

      const contentResult = await this.contentModel.create(contentData, {
        transaction: t,
      });
      const entryData = entries.map((item, index) => ({
        ...item,
        contentId: contentResult.id,
        order: index,
        userId: this.request.user.userId,
      }));
      const entryResult = await this.entryModel.bulkCreate(entryData, {
        transaction: t,
      });
      await this.customization.update(
        { sectionOrder },
        {
          where: {
            resumeId,
            userId: this.request.user.userId,
          },
          transaction: t,
        },
      );
      await t.commit();
      return { contentId: contentResult.id, entryId: entryResult[0]?.id };
    } catch (error) {
      // 如果执行到达此行,则抛出错误,回滚事务
      await t.rollback();
      throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }

  async editContent(params: editContentParamsDto): Promise<any> {
    const { resumeId, id, ...rest } = params;
    const where = {
      id,
      resumeId,
      userId: this.request.user.userId,
    };
    const contentResult = await this.contentModel.update(rest, {
      where,
    });
    return responseMessage({ contentId: contentResult });
  }

  async editSectionName(params: EditSectionNameDto): Promise<any> {
    const { id, displayName, iconKey } = params;
    const where = {
      id,
      userId: this.request.user.userId,
    };
    const contentResult = await this.contentModel.update(
      { displayName, iconKey },
      {
        where,
      },
    );
    return responseMessage({ contentId: contentResult });
  }

  async delContent(params: delContentParamsDto): Promise<any> {
    const { resumeId, id } = params;
    const where = {
      id,
      resumeId,
      userId: this.request.user.userId,
    };
    const t = await this.sequelize.transaction();
    try {
      await this.contentModel.destroy({
        where,
      });
      // await this.customization.update(
      //   { sectionOrder },
      //   {
      //     where: {
      //       resumeId,
      //       userId: this.request.user.userId,
      //     },
      //     transaction: t,
      //   },
      // );
      await t.commit();
      return true;
    } catch (e) {
      return responseMessage(false, '更新失败', -1);
    }
  }

  async addEntry(params: EntryDto): Promise<any> {
    try {
      const entryResult = await this.entryModel.create({
        ...params,
        userId: this.request.user.userId,
      });
      return { entryId: entryResult.id };
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async delEntry(params: EntryDto): Promise<any> {
    const { contentId, id } = params;
    try {
      const where = {
        userId: this.request.user.userId,
        contentId,
        id,
      };
      const res = await this.entryModel.destroy({
        where,
      });
      if (res) {
        return true;
      } else {
        return responseMessage(null, '更新失败', -1);
      }
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async deleteResume(params: EntryDto): Promise<any> {
    const { id } = params;
    try {
      const where = {
        userId: this.request.user.userId,
        id,
      };
      const res = await this.resumeModel.update(
        {
          deleted: 1,
        },
        {
          where,
        },
      );
      if (res) {
        return true;
      } else {
        return responseMessage(null, '更新失败', -1);
      }
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async editEntry(params: EntryDto): Promise<any> {
    try {
      const where = { id: params.id, userId: this.request.user.userId };
      await this.entryModel.update({ ...params }, { where });
      return true;
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async toggleHideEntry(params: ToggleHideEntryDto): Promise<any> {
    try {
      const { id, isHidden } = params;
      const where = { id, userId: this.request.user.userId };
      await this.entryModel.update({ isHidden }, { where });
      return true;
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async sortEntry(params: SortEntryDto): Promise<any> {
    try {
      const { contentId, newOrder } = params;
      const entries = [];
      if (newOrder && newOrder.length) {
        const t = await this.sequelize.transaction();
        try {
          for (let i = 0; i < newOrder.length; i++) {
            const orderId = newOrder[i];
            const where = {
              id: orderId,
              contentId: contentId,
              userId: this.request.user.userId,
            };

            const res = await this.entryModel.update(
              { order: i },
              { where, transaction: t },
            );
            entries.push(res[0]);
          }
          await t.commit();
        } catch (error) {
          await t.rollback();
          throw new HttpException(error, HttpStatus.INTERNAL_SERVER_ERROR);
        }
      }
      if (!entries.filter((t) => t).length) {
        return responseMessage(null, '更新失败', -1);
      }
      return null;
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async updatePersonalDetail(params: PersonalDetailDto): Promise<any> {
    try {
      const { id, ...rest } = params;
      const where = {
        id,
        userId: this.request.user.userId,
      };
      await this.resumeModel.update(
        { ...rest },
        {
          where,
        },
      );
      return true;
    } catch (e) {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async applyTemplate(params: ApplyTemplateDto): Promise<any> {
    try {
      const { templateId, id } = params;
      const temp = await this.templateService.getTemplate({
        id: templateId,
      });
      const where = {
        id,
        userId: this.request.user.userId,
      };
      const res = await this.customization.update(
        {
          ...temp.customization,
          templateId,
        },
        {
          where,
        },
      );
      if (res[0] === 1) {
        return true;
      } else {
        return responseMessage(null, '更新失败', -1);
      }
    } catch {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async updateCustomization(params: UpdateCustomizationDto) {
    try {
      const { id, updates } = params;
      const where = {
        id,
        userId: this.request.user.userId,
      };
      if (!updates.length) return;
      const pathObj = {};
      const values = {};
      updates.forEach((item) => {
        const [attr, ...rest] = item.path.split('.');
        values[item.path] = item.value;
        if (pathObj[attr]) {
          pathObj[attr].push(rest);
        } else {
          pathObj[attr] = [rest];
        }
      });

      const needUpdateKeys = Object.keys(pathObj);
      if (needUpdateKeys && needUpdateKeys.length) {
        const result =
          (await this.customization.findOne({
            attributes: { include: needUpdateKeys },
            where,
          })) || {};
        const newValue = {};

        for (let i = 0; i < Object.keys(pathObj).length; i++) {
          const field = Object.keys(pathObj)[i];
          let data = result[field];
          if (!data) {
            const template = await this.templateService.getTemplate({
              id: 1,
            });
            const initCustomization = template.customization;
            data = initCustomization[field];
          }
          pathObj[field].forEach((item) => {
            if (item && item.length) {
              updateValue(data, item, values[[field, ...item].join('.')]);
            } else {
              data = values[field];
            }
          });
          newValue[field] = data;
        }
        if (newValue && Object.keys(newValue).length) {
          const res = await this.customization.update(
            {
              ...newValue,
            },
            {
              where,
            },
          );
          if (res.length) {
            return true;
          }
        }
      }
      return responseMessage(null, '更新失败', -1);
    } catch (e) {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async addCustomization(params: addCustomizationDto) {
    const { customization, resumeId } = params;
    if (!this.request.user?.userId) {
      return responseMessage({}, '用户不存在!', -1);
    }
    try {
      // const template = await this.templateService.getTemplate({
      //   id: customization.templateId,
      // });
      await this.customization.create({
        ...customization,
        // defaultSectionOrder: (template.customization as any)
        //   .defaultSectionOrder,
        resumeId,
        userId: this.request.user.userId,
      });
      return true;
    } catch (e) {
      return responseMessage(null, '更新失败', -1);
    }
  }

  async downloadFile(params) {
    const { htmlString, resumeId, templateId } = params;
    try {
      await this.addDownloadLog({
        userId: this.request.user?.userId || null,
        templateId,
        resumeId: resumeId,
      });
    } catch (e) {}
    const pdf = await generatePdf(htmlString);
    if (!pdf) return responseMessage(null, '导出失败', -1);
    try {
      const filePath = path.join(
        process.cwd(),
        'downloads',
        `${sd.format(new Date(), 'YYYY-MM-DD HH:mm')}_${this.request.user?.userId}_${resumeId}_.pdf`,
      );
      await fs.writeFileSync(filePath, pdf);
    } catch (e) {
      console.log(e);
    }
    return pdf;
  }

  async addDownloadLog({ userId, resumeId, templateId }) {
    try {
      await this.downloadLog.create({
        templateId,
        resumeId,
        userId,
        previewUrl: 'https://www.jianli101.com/preview/' + resumeId,
      });
    } catch (e) {}
    return true;
  }

  async downloadFileById(params) {
    const { resumeId } = params;
    const resume = await this.resumeModel.findOne({
      attributes: ['id'],
      // 联表查询
      where: {
        id: resumeId,
        userId: this.request.user?.userId,
      },
    });
    if (!resume) {
      return '导出失败';
    }

    const url =
      process.env.NODE_ENV === 'development'
        ? `http://localhost:8080/serverpreview/${resumeId}`
        : `http://nextapp:3004/serverpreview/${resumeId}`;

    try {
      await this.addDownloadLog({
        userId: this.request.user?.userId || null,
        templateId: '',
        resumeId: resumeId,
      });
    } catch (e) {}
    const pdf = await generatePdfByUrl(url);
    try {
      const filePath = path.join(
        process.cwd(),
        'downloads',
        `${sd.format(new Date(), 'YYYY-MM-DD HH:mm')}_${this.request.user?.userId}_${resumeId}_.pdf`,
      );
      await fs.writeFileSync(filePath, pdf);
    } catch (e) {
      console.log(e);
    }
    return pdf;
  }
}
